export function patch(oldVnode,vnode) {
    // 原则就是将虚拟节点转换成真实的节点
    // 第一次渲染的话,oldValue就是一个真实的dom
    if(!oldVnode){
        createEl(vnode)
    }
    if(oldVnode.nodeType===1){
        let el = createEl(vnode)
        console.log(el);
        let parentEl = oldVnode.parentNode // 获取父节点
        // 创建新的DOM插入父节点
        parentEl.insertBefore(el,oldVnode.nextsibling)
        // 删除
        parentEl.removeChild(oldVnode)
        return el 
    } else {
        // 元素不一样的话
        if(oldVnode.tag!==vnode.tag){
           return oldVnode.el.parentNode.replaceChild(createEl(vnode),oldVnode.el)
        }
        // 2 标签一样 text 属性 不一样
        // 例如 <div>1</div> => <div>2</div> tag:undefined
        if(!oldVnode.tag){
            if(oldVnode.text!==vnode.text){
                oldVnode.el.textContent = vnode.text
            }
        }

        // 2.1属性(标签一样) 属性不一样<div id="a">1</div> <div id="2">2</div>
        let el = vnode.el = oldVnode.el
        updataRpors(vnode,oldVnode.data)

        // diff处理子元素
        let oldChildren = oldVnode.children || []
        let newChildren = vnode.children || []
         // 3. 老的有children 新的有children
        if(oldChildren.length>0&&newChildren.length>0){      
            updataChild(oldChildren,newChildren,el)
        } else if(oldChildren.length>0){ // 2. 老的没有children 新的有children
            el.innerHTML = ''
        } else if(newChildren.length>0){ // 1. 老的有元素有children 新的没有children
            for(let i = 0; i<newChildren.length;i++){
                let child = newChildren[i]
                // 添加到真实dom中
                el.appendChild(createEl(child))
            }
        }
    }
}


// 添加属性

function updataRpors(vnode,oldProps={}) {
    let newProps = vnode.data ||{} // 获取到当前新节点的属性
    let el = vnode.el // 获取当前真实节点 {}

    // 1.老的有属性,新的没有属性
    for(let key in oldProps){
        // 属性在旧节点存在,新节点不存在
        if(!newProps[key]){
            // 删除(真实dom中移除属性)
            el.removeAttribute[key]
        }
    }
    // 2. 处理样式 例如: old style="color:red"  new style="background:blue"
    let newStyle = newProps.style || {} // 获取新的样式
    let oldStyle = oldProps.style || {} // 获取老的样式
    for(let key in oldStyle){
        if(!newStyle[key]){
            el.style = ''
        }
    }


    for (const key in newProps) {
        // 处理style属性
        if(key === 'style'){
            for(let styleName in newProps.style){
                el.style[styleName] = newProps.style[styleName]
            }
        } else if(key === 'class'){
            el.className = newProps.class
        } else {
            el.setAttribute(key,newProps[key])
        }
    }
}


//创建元素
 export function createEl(vnode) { // vnode是一个对象
    // {tag:'div',data:{},children}
    let {vm,tag,children,key,data,text} = vnode
    // 如果tag 是字符串的话，如果元素存在，创建元素
    if(typeof tag === 'string'){
        // 创建组件的正式dom
        if(createComponent(vnode)){
            console.log(vnode,'222222222222');
            return vnode.componentInstance.$el
        } else {
            // 创建元素
            vnode.el = document.createElement(tag) // 创建div
            // 初次渲染,添加属性
            updataRpors(vnode)
            // children [] 存在
            children&&children.forEach(child => {
            // 给元素中添加子集
            // 递归调用createEl方法
            vnode.el.appendChild(createEl(child))
            });
        }
    } else {
        vnode.el = document.createTextNode(text)
    }
    return vnode.el
}

// 创建组件的真实dom
function createComponent(vnode) {
    let i = vnode.data
    // 如果vnode.data中有hook切有init.就表示是组件
    if((i = i.hook)&&(i=  i.init)){
        i(vnode) // 调用vnode.data.hook.init方法,创建实例
    }
    if(vnode.componentInstance){
        return true
    }  
     return false
    
}

function updataChild(oldChildren,newChildren,el) {
    // 采用四指针的思想
    let oldStartIndex = 0 // 旧前索引
    let oldStartVNode = oldChildren[oldStartIndex] // 旧前索引
    let oldEndIndex = oldChildren.length-1 // 旧后索引
    let oldEndVNode = oldChildren[oldEndIndex] // 旧后节点

    let newStartIndex = 0 // 新前索引
    let newStartVNode = newChildren[newStartIndex] // 新前索引
    let newEndIndex = newChildren.length-1 // 新后索引
    let newEndVNode = newChildren[newEndIndex] // 新后节点
    let keyMap = null; 
    // 根据key和tag是否相同，判断是否为同一元素
    function isSomeVnode(oldcontent,newcontent) {
        return (oldcontent.tag === newcontent.tag && oldcontent.key === newcontent.key)
    }

    while(oldStartIndex<=oldEndIndex && newStartIndex<=newEndIndex){
        
        // 比对子元素，排除节点不存在，或为undefined的节点。
        // 因为存在已经修改过的节点，被标记为了undefined
        if(oldStartVNode == null||oldChildren[oldStartIndex] == undefined) { 
            oldStartVNode = oldChildren[++oldStartIndex]
        } else if(oldEndVNode == null||oldChildren[oldEndIndex] == undefined) {
            oldEndVNode = oldChildren[--oldEndIndex]
        } else if(newStartVNode == null||newChildren[newStartIndex] == undefined) {
            newStartVNode = newChildren[++newStartIndex]
        } else if(newEndVNode == null||newChildren[newStartIndex] == undefined) {
            newEndVNode = newChildren[--newStartIndex]
        } else if(isSomeVnode(oldStartVNode,newStartVNode)){
            // 旧前和新前命中
            patch(oldStartVNode,newStartVNode)
            oldStartVNode = oldChildren[++oldStartIndex]
            newStartVNode = newChildren[++newStartIndex];
        } else if(isSomeVnode(oldEndVNode,newEndVNode)){
            // 旧后与新后命中
            patch(oldEndVNode,newEndVNode)
            oldEndVNode = oldChildren[--oldEndIndex];
            newEndVNode = newChildren[--newEndIndex];
        } else if(isSomeVnode(oldStartVNode,newEndVNode)){
            // 命中旧前与新后
            patch(oldStartVNode,newEndVNode)
            // 在此还要有一操作就是移动
            el.insertBefore(oldStartVNode.el,oldEndVNode.el.nextSibling) 
            oldStartVNode = oldChildren[++oldStartIndex]
            newEndVNode = newChildren[--newEndIndex]
        } else if(isSomeVnode(oldEndVNode,newStartVNode)){
            // 命中旧后与新前
            patch(oldEndVNode,newStartVNode)
            el.insertBefore(oldEndVNode.el,oldStartVNode.el)
            oldEndVNode = oldChildren[--oldEndIndex]
            newStartVNode = newChildren[++newStartIndex]
        } else {
            // 四种情况都没有命中
            console.log('四种都没有命中');
            if(!keyMap){
                keyMap = {}
                for (let i = oldStartIndex; i <= oldEndIndex; i++) {
                    const key = oldChildren[i].key
                    if(key != undefined){
                        keyMap[key] = i;
                    }
                }
            }
            console.log(keyMap);
            // 寻找当前这项newStartVNode在keyMap的位置序号
            const idxInOld = keyMap[newStartVNode.key]
            if(idxInOld == undefined){ //如果新前节点的key不存在于旧的虚拟节点数组中，代码当前新前节点需要新增。新增到旧前之前
                // 被加入的项（就是newStartVnode）现在还不是dom节点
                el.insertBefore(createEl(newStartVNode),oldStartVNode.el)
            } else {
                // 如果不是全新的话，要移动。
                // 如果新前节点的值存在于，keyMap。
                // 代表移动节点。并且要把这项设置为undefined
                // 移动的规则就是把找到的这个旧节点移动到未处理的旧节点之前
                const elmToMove = oldChildren[idxInOld]
                if(elmToMove.el.nodeType==1){
                    patch(elmToMove,newStartVNode)
                    // 把这项设置为undefined，表示已经处理了这项
                    // 把旧的节点变为undefined，表示处理过改节点了
                    oldChildren[idxInOld] = undefined
                    //移动  调用insertBefore
                    // 也是找到节点，移动到新前之前
                    el.insertBefore(elmToMove.el,oldStartVNode.el)
                }
            }
            // 指针下移
            newStartVNode = newChildren[++newStartIndex]
        }
    }

    // 循环结束，如果新节点还没遍历完毕，所以有新增的节点
    if(newStartIndex <= newEndIndex){
        for(let i = newStartIndex; i<=newEndIndex;i++){
            // 创建节点，追加进去
            el.insertBefore(createEl(newChildren[newStartIndex]),oldChildren[oldStartIndex]?oldChildren[oldStartIndex].el:null)
        }
    } else if(oldStartIndex<=oldEndIndex){
        // 代表上面的循环结束后，旧节点存在需要删除的元素，进行删除
        for (let i = oldStartIndex; i <= oldEndIndex; i++) {
            if(oldChildren[i]){
                el.removeChild(oldChildren[i].el)
            }
        }
    }
}